package com.hmall.gateway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    private final NacosConfigManager nacosConfigManager; //nacos配置管理器
    private final RouteDefinitionWriter routeDefinitionWriter; //利用RouteDefinitionWriter进行路由更新
    private final String dateId="gateway-routes.json";
    private final String group="DEFAULT_GROUP"; //nacos默认分组
    private final Set<String> routeIds=new HashSet<>();//记录路由id

    @PostConstruct//标记该方法在bean初始化后进行
    public void initRouteConfigListener() throws NacosException {
        // 1.项目启动时，先拉取一下配置，并且添加配置监听器
        //监听路由配置，拉取和配置监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dateId, group, 5000/*5秒*/, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        // 2.监听到配置变更，需要去更新路由表
                        updateConfigInfo(configInfo);
                    }
                });

        // 3.第一次读取到配置变更，需要去更新路由表
        updateConfigInfo(configInfo);
    }

    public void updateConfigInfo(String configInfo){
        // 监听到路由信息后，利用RouteDefinitionWriter进行路由更新
        log.debug("监听到路由配置信息: {}", configInfo);

        // 1.解析配置信息，转为RouteDefinition对象
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);

        // 2.1 删除旧的路由表
        for (String routeId : routeIds) {
            routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
        }
        //2.2 清空路由id表
        routeIds.clear();

        // 3.更新路由表
        for (RouteDefinition routeDefinition : routeDefinitions) {
            // 3.1 更新路由表
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            // 3.1 记录路由id，便于下一次更新时删除
            routeIds.add(routeDefinition.getId());
        }
    }

}


